{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d93110f5",
   "metadata": {},
   "source": [
    "# Runnables 구조 검토\n",
    "\n",
    "LCEL로 `runnable` 을 생성한 후에는 종종 이를 검사하여 어떤 일이 일어나고 있는지 더 잘 파악하고 싶을 것입니다.\n",
    "\n",
    "이 노트북에서는 이를 수행하는 몇 가지 방법을 다룹니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6e4d8cf8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# API 키를 환경변수로 관리하기 위한 설정 파일\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# API 키 정보 로드\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82c76648",
   "metadata": {},
   "outputs": [],
   "source": [
    "# LangSmith 추적을 설정합니다. https://smith.langchain.com\n",
    "# !pip install langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름을 입력합니다.\n",
    "logging.langsmith(\"CH13-LCEL-Advanced\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "312a1edb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# !pip install -qU faiss-cpu tiktoken\n",
    "\n",
    "# 그래프를 그리기 위한 라이브러리 설치\n",
    "# !pip install -qU grandalf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00b3453b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "from langchain.vectorstores import FAISS\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnableLambda, RunnablePassthrough\n",
    "from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n",
    "\n",
    "vectorstore = FAISS.from_texts(\n",
    "    # 텍스트 데이터로부터 FAISS 벡터 저장소를 생성합니다.\n",
    "    [\"Teddy is an AI engineer who loves programming!\"],\n",
    "    embedding=OpenAIEmbeddings(),\n",
    ")\n",
    "\n",
    "# 벡터 저장소를 기반으로 retriever를 생성합니다.\n",
    "retriever = vectorstore.as_retriever()\n",
    "\n",
    "template = \"\"\"Answer the question based only on the following context:\n",
    "{context}  \n",
    "\n",
    "Question: {question}\"\"\"\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(\n",
    "    template\n",
    ")  # 템플릿을 기반으로 ChatPromptTemplate을 생성합니다.\n",
    "\n",
    "model = ChatOpenAI(model=\"gpt-4o-mini\")  # ChatOpenAI 모델을 초기화합니다.\n",
    "\n",
    "# chain 을 생성합니다.\n",
    "chain = (\n",
    "    # 검색 컨텍스트와 질문을 지정합니다.\n",
    "    {\"context\": retriever, \"question\": RunnablePassthrough()}\n",
    "    | prompt  # 프롬프트를 생성합니다.\n",
    "    | model  # 언어 모델을 실행합니다.\n",
    "    | StrOutputParser()  # 출력 결과를 문자열로 파싱합니다.\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4aebcc63",
   "metadata": {},
   "source": [
    "## 그래프 구성 확인\n",
    "\n",
    "runnable의 그래프를 얻을 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90b39c06",
   "metadata": {},
   "source": [
    "`chain.get_graph()` 메서드는 체인의 실행 그래프를 반환합니다.\n",
    "\n",
    "- 이 메서드는 체인의 각 노드와 노드 간의 연결을 나타내는 그래프 객체를 반환합니다.\n",
    "- 그래프의 노드는 체인의 각 단계를 나타내며, 에지(edge)는 단계 간의 데이터 흐름을 나타냅니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "69d35d4d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 체인의 그래프에서 노드를 가져옵니다.\n",
    "chain.get_graph().nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8aa8eedb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 체인의 그래프에서 엣지를 가져옵니다.\n",
    "chain.get_graph().edges"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69801031",
   "metadata": {},
   "source": [
    "## 그래프 출력\n",
    "\n",
    "그래프를 출력하면 이해하기 쉬운 형태로 표현할 수 있습니다.\n",
    "\n",
    "비록 출력 결과가 매우 읽기 쉽지는 않지만, 출력을 통해 보다 이해하기 쉬운 형태로 그래프를 확인할 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a22c179d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 체인의 그래프를 ASCII 형식으로 출력합니다.\n",
    "chain.get_graph().print_ascii()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "413a6d11",
   "metadata": {},
   "source": [
    "## 프롬프트 가져오기\n",
    "\n",
    "체인에서 중요한 부분은 사용되는 프롬프트입니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ba41b1c",
   "metadata": {},
   "source": [
    "`chain.get_prompts()` 메서드는 체인에서 사용되는 프롬프트(prompt) 객체의 리스트를 반환합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8deea4a",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain.get_prompts()  # 체인에서 사용되는 프롬프트를 가져옵니다."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py-test",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
